<!Doctype html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<title>4.2.3 Service精通 | 菜鸟教程</title>

  <meta name='robots' content='max-image-preview:large' />
<link rel='stylesheet' id='classic-theme-styles-css' href='../0/classic-themes.min.css?ver=6.2' type='text/css' media='all' />
<link rel="canonical" href="../w3cnote/android-tutorial-service-3.html" />
<meta name="keywords" content="4.2.3 Service精通">
<meta name="description" content="本节引言：  本节，我们继续来研究Service(服务)组件，本节将会学习下Android中的AIDL跨进程通信的一些 概念，并不深入到源码层次，暂时知道是什么，会用即可！开始本节内容~ 本节对应官方文档：Binder    1.Binder机制初涉    1）IBinder和Binder是什么鬼？  我们来看看官方文档怎么说：    中文翻译：  IBinder是远程对象的基本接口，是饿了高性能而设计的轻量级远程调用机制的核心部分。但..">
		
	<link rel="shortcut icon" href="https://static.runoob.com/images/favicon.ico">
	<link rel="stylesheet" href="../0/style.css?v=1.170" type="text/css" media="all" />	
	<link rel="stylesheet" href="../0/font-awesome.min.css" media="all" />	
  <!--[if gte IE 9]><!-->
  <script src=""></script>
  <!--<![endif]-->
  <!--[if lt IE 9]>
     <script src=""></script>
     <script src=""></script>
  <![endif]-->
  <link rel="apple-touch-icon" href="https://static.runoob.com/images/icon/mobile-icon.png"/>
  <meta name="apple-mobile-web-app-title" content="菜鸟教程">
</head>
<body>

<!--  头部 -->
<div class="container logo-search">

  <div class="col search row-search-mobile">
    <form action="index.php">
      <input class="placeholder" placeholder="搜索……" name="s" autocomplete="off">
      
    </form>
  </div>

  <div class="row">
    <div class="col logo">
      <h1><a href="../">菜鸟教程 -- 学的不仅是技术，更是梦想！</a></h1>
    </div>
        <div class="col right-list"> 
    <button class="btn btn-responsive-nav btn-inverse" data-toggle="collapse" data-target=".nav-main-collapse" id="pull" style=""> <i class="fa fa-navicon"></i> </button>
    </div>
        
    <div class="col search search-desktop last">
      <div class="search-input" >
      <form action="//www.runoob.com/" target="_blank">
        <input class="placeholder" id="s" name="s" placeholder="搜索……"  autocomplete="off" style="height: 44px;">
      </form>
      
      </div>
    </div>
  </div>
</div>



<!-- 导航栏 -->
<div class="container navigation">
    <div class="row">
        <div class="col nav">
            

                        <ul class="pc-nav" id="note-nav">
                <li><a href="../">首页</a></li>
                <li><a href="../w3cnote">笔记首页</a></li>
                <li><a href="../w3cnote/android-tutorial-intro.html" title="Android 基础入门教程">Android</a></li>
                <li><a href="../w3cnote/es6-tutorial.html" title="ES6 教程">ES6 教程</a></li>
                <li><a href="../w3cnote/ten-sorting-algorithm.html" title="排序算法">排序算法</a></li>
                <li><a href="../w3cnote/hadoop-tutorial.html" title="Hadoop 教程">Hadoop</a></li>
                <li><a href="../w3cnote/zookeeper-tutorial.html" title="Zookeeper 教程">Zookeeper</a></li>
                <li><a href="../w3cnote/verilog-tutorial.html" title="Verilog 教程">Verilog</a></li>
                <li><a href="../w3cnote_genre/code" title="编程技术">编程技术</a></li> 
                <li><a href="../w3cnote_genre/coderlife" title="程序员人生">程序员人生</a></li>
                
                <!--<li><a href="javascript:;" class="runoob-pop">登录</a></li>
                
                
                        <li>
                <a style="font-weight:bold;" href="../linux/linux-tutorial.html#yunserver" target="_blank" onclick="_hmt.push(['_trackEvent', 'aliyun', 'click', 'aliyun'])" title="kkb">云服务器</a>
                </li>
                <li><a href="http://gk.link/a/104mQ" target="_blank" style="font-weight: bold;"onclick="_hmt.push(['_trackEvent', '极客时间', 'click', 'jike'])" title="我的圈子">极客时间</a></li>
            
                
                <li><a target="_blank" href="../shoppinglist" rel="nofollow">知识店铺</a></li> 
        -->
            </ul>
                        
              
            <ul class="mobile-nav">
                <li><a href="../w3cnote">首页</a></li>
                <li><a href="../w3cnote_genre/android" target="_blank" title="Android 基础入门教程">Android</a></li>
                <li><a href="../w3cnote/es6-tutorial.html" target="_blank" title="ES6 教程">ES6</a></li>
                <li><a href="../w3cnote_genre/joke" target="_blank" title="程序员笑话">逗乐</a></li>
                
                <a href="javascript:void(0)" class="search-reveal">Search</a> 
            </ul>
            
        </div>
    </div>
</div>


<!--  内容  -->
<div class="container main">
	<div class="row">

		<!--  Android 基础入门教程 start  -->
	<div class="col left-column" style="display:none;">
		<div class="tab">Android 基础入门教程</div>
		<div class="sidebar-box gallery-list">
			<div class="design" id="leftcolumn">  
			</div> 
		</div> 
	</div>
	<!--  Android 基础入门教程 end  -->
		<div class="col middle-column big-middle-column">
	 			<div class="article">
			<div class="article-heading">
				<h2>4.2.3 Service精通</h2>				<h3><em>分类</em> <a href="../w3cnote_genre/android" title="Android 基础入门教程" >Android 基础入门教程</a> </h3>
			</div>
			<div class="article-body note-body">
				<div class="article-intro">
					<h2>本节引言：</h2>

<blockquote><p>本节，我们继续来研究Service(服务)组件，本节将会学习下Android中的AIDL跨进程通信的一些
概念，并不深入到源码层次，暂时知道是什么，会用即可！开始本节内容~
本节对应官方文档：<a href="http://developer.android.com/intl/zh-cn/reference/android/os/Binder.html">Binder</a></p></blockquote>

<hr />

<h2>1.Binder机制初涉</h2>

<hr />

<h3>1）IBinder和Binder是什么鬼？</h3>

<p>我们来看看官方文档怎么说：</p>

<p><img decoding="async" src="//www.runoob.com/wp-content/uploads/2015/08/12024442.jpg" alt="" /></p>

<p>中文翻译：</p>

<blockquote><p>IBinder是远程对象的基本接口，是饿了高性能而设计的轻量级远程调用机制的核心部分。但他
不仅用于远程调用，也用于进程内调用。该接口定义了与远程对象间交互的协议。但不要直接实现
这个接口，而是<strong>继承</strong>(extends)<strong>Binder</strong>。</p>

<p>IBinder主要的API是<strong>transact()</strong>，与之对应的API是<strong>Binder.onTransact()</strong>。通过前者，你能
想远程IBinder对象发送发出调用，后者使你的远程对象能够响应接收到的调用。IBinder的API都是
<strong>Syncronous(同步)</strong>执行的，比如<strong>transact()</strong>直到对方的<strong>Binder.onTransact()</strong>方法调用玩
后才返回。
调用发生在进程内时无疑是这样的，而在进程间时，在<strong>IPC</strong>的帮助下，也是同样的效果。</p>

<p>通过<strong>transact()</strong>发送的数据是<strong>Parcel</strong>，Parcel是一种一般的缓冲区，除了有数据外还带有
一些描述它内容的元数据。元数据用于管理IBinder对象的引用，这样就能在缓冲区从一个进程移动
到另一个进程时保存这些引用。这样就保证了当一个IBinder被写入到Parcel并发送到另一个进程中，
如果另一个进程把同一个IBinder的引用回发到原来的进程，那么这个原来的进程就能接收到发出的
那个IBinder的引用。这种机制使IBinder和Binder像唯一标志符那样在进程间管理。</p>

<p>系统为每个进程维护一个存放交互线程的线程池。这些交互线程用于派送所有从另外进程发来的IPC
调用。例如：当一个IPC从进程Ａ发到进程Ｂ，Ａ中那个发出调用的线程(这个应该不在线程池中)就阻塞
在<strong>transact()</strong>中了。进程Ｂ中的交互线程池中的一个线程接收了这个调用，它调用
<strong>Binder.onTransact()</strong>，完成后用一个Parcel来做为结果返回。然后进程Ａ中的那个等待的线程在
收到返回的Parcel后得以继续执行。实际上，另一个进程看起来就像是当前进程的一个线程，
但不是当前进程创建的。</p>

<p>Binder机制还支持进程间的递归调用。例如，进程Ａ执行自己的IBinder的transact()调用进程Ｂ
的Binder，而进程Ｂ在其Binder.onTransact()中又用transact()向进程Ａ发起调用，那么进程Ａ
在等待它发出的调用返回的同时，还会用Binder.onTransact()响应进程Ｂ的transact()。
总之Binder造成的结果就是让我们感觉到跨进程的调用与进程内的调用没什么区别。</p>

<p>当操作远程对象时，你经常需要查看它们是否有效，有三种方法可以使用：</p>

<ul>
<li>１ transact()方法将在IBinder所在的进程不存在时抛出RemoteException异常。</li>
<li>２ 如果目标进程不存在，那么调用pingBinder()时返回false。</li>
<li>３ 可以用linkToDeath()方法向IBinder注册一个IBinder.DeathRecipient，
在IBinder代表的进程退出时被调用。</li>
</ul>


<p>PS:中文翻译摘自 : <a href="http://blog.csdn.net/niu_gao/article/details/6453218">Android开发：什么是IBinder </a></p></blockquote>

<p>好吧，估计你看完上这一串东西可能云里雾里的，这里简单的小结下：</p>

<p><strong>IBinder是Android给我们提供的一个进程间通信的一个接口，而我们一般是不直接实现这个接口的，</strong>
<strong>而是通过继承Binder类来实现进程间通信！是Android中实现IPC(进程间通信)的一种方式！</strong></p>

<hr />

<h3>2）Binder机制浅析</h3>

<blockquote><p>Android中的Binder机制由一系列系统组件构成：
<strong>Client、Server、Service Manager和Binder驱动程序</strong></p></blockquote>

<p>大概调用流程如下，另外Service Manager比较复杂，这里并不详细研究！</p>

<p><img decoding="async" src="//www.runoob.com/wp-content/uploads/2015/08/40423368.jpg" alt="" /></p>

<p><strong>流程解析：</strong></p>

<blockquote><p><strong>-></strong> Client调用某个代理接口中的方法时，代理接口的方法会将Client传递的参数打包成Parcel对象；<br>
<strong>-></strong> 然后代理接口把该Parcel对象发送给内核中的Binder driver；；<br>
<strong>-></strong> 然后Server会读取Binder Driver中的请求数据，假如是发送给自己的，解包Parcel对象，
处理并将结果返回；<br>
PS:代理接口中的定义的方法和Server中定义的方法是一一对应的，
另外，整个调用过程是一个同步的，即Server在处理时，Client会被Block(锁)住!
而这里说的代理接口的定义就是等下要说的<strong>AIDL</strong>(Android接口描述语言)！</p></blockquote>

<hr />

<h3>3）为何Android使用Binder机制来实现进程间的通信？</h3>

<blockquote><ol>
<li><strong>可靠性</strong>：在移动设备上，通常采用基于Client-Server的通信方式来实现互联网与设备间的内部通信。目前linux支持IPC包括传统的管道，System V IPC，即消息队列/共享内存/信号量，以及socket中只有socket支持Client-Server的通信方式。Android系统为开发者提供了丰富进程间通信的功能接口，媒体播放，传感器，无线传输。这些功能都由不同的server来管理。开发都只关心将自己应用程序的client与server的通信建立起来便可以使用这个服务。毫无疑问，如若在底层架设一套协议来实现Client-Server通信，增加了系统的复杂性。在资源有限的手机 上来实现这种复杂的环境，可靠性难以保证。</li>
<li><strong>传输性能</strong>：socket主要用于跨网络的进程间通信和本机上进程间的通信，但传输效率低，开销大。消息队列和管道采用存储-转发方式，即数据先从发送方缓存区拷贝到内核开辟的一块缓存区中，然后从内核缓存区拷贝到接收方缓存区，其过程至少有两次拷贝。虽然共享内存无需拷贝，但控制复杂。比较各种IPC方式的数据拷贝次数。共享内存：0次。Binder：1次。Socket/管道/消息队列：2次。</li>
<li><strong>安全性</strong>：Android是一个开放式的平台，所以确保应用程序安全是很重要的。Android对每一个安装应用都分配了UID/PID,其中进程的UID是可用来鉴别进程身份。传统的只能由用户在数据包里填写UID/PID，这样不可靠，容易被恶意程序利用。而我们要求由内核来添加可靠的UID。
所以，出于可靠性、传输性、安全性。android建立了一套新的进程间通信方式。
——摘自:<a href="http://www.linuxidc.com/Linux/2012-07/66195.htm">Android中的Binder机制的简要理解</a></li>
</ol>
</blockquote>

<p>当然，作为一个初级的开发者我们并不关心上述这些，Binder机制给我们带来的最直接的好处就是：
<strong>我们无需关心底层如何实现，只需按照AIDL的规则，自定义一个接口文件，</strong>
<strong>然后调用调用接口中的方法，就可以完成两个进程间的通信了！</strong></p>

<hr />

<h2>2.AIDL使用详解</h2>

<hr />

<h3>1）AIDL是什么？</h3>

<blockquote><p>嘿嘿，前面我们讲到<strong>IPC</strong>这个名词，他的全名叫做：<strong>跨进程通信(interprocess communication)</strong>，
因为在Android系统中,个个应用程序都运行在自己的进程中,进程之间一般是无法直接进行数据交换的,
而为了实现跨进程，Android给我们提供了上面说的Binder机制，而这个机制使用的接口语言就是:
<strong>AIDL</strong>(Android Interface Definition Language)，他的语法很简单，而这种接口语言并非真正的编程
语言，只是定义两个进程间的通信接口而已！而生成符合通信协议的Java代码则是由Android SDK的
platform-tools目录下的<strong>aidl.exe</strong>工具生成，生成对应的接口文件在:gen目录下，一般是:Xxx.java的接口！
而在该接口中包含一个<strong>Stub</strong>的内部类，该类中实现了在该类中实现了<strong>IBinder</strong>接口与自定义的通信接口,
这个类将会作为远程Service的回调类——实现了IBinder接口,所以可作为Service的onBind( )方法的返回值！</p></blockquote>

<hr />

<h3>2）AIDL实现两个进程间的简单通信</h3>

<p>在开始编写AIDL接口文件前，我们需要了解下编写AIDL的一些注意事项：</p>

<p><strong>AIDL注意事项：</strong></p>

<blockquote><ul>
<li>接口名词需要与aidl文件名相同</li>
<li>接口和方法前面<strong>不要加访问权限修饰符</strong>：public ,private,protected等，也不能用static final!</li>
<li>AIDL默认支持的类型包括<strong>Java基本类型</strong>，<strong>String</strong>，<strong>List</strong>，<strong>Map</strong>，<strong>CharSequence</strong>，除此之外的其他类型都
需要import声明，对于使用自定义类型作为参数或者返回值，自定义类型需要实现Parcelable接口，
详情请看后面的传递复杂数据类型</li>
<li>自定义类型和AIDL生成的其它接口类型在aidl描述文件中，应该显式import，即便在该类和定义
的包在同一个包中。</li>
</ul>

<p>另外，如果编写aidl你用的编译器是:Eclipse的话要注意：
不要直接new file然后建立哦!这样的话是打不开文件,从而不能编写代码哦！<br>
①直接新建一个txt文件,编写好后保存为.aidl格式,然后复制到对应路径下<br>
②因为aidl和接口类似,所以直接new interface,编写好内容后,来到对应java文件所在目录下修改文件后缀名;</p>

<p>假如你使用的是Android Studio的话，不同于Eclipse，如果你按照Eclipse那样创建一个AIDL文件，会发现
并没有编译生成对应的XXX.java文件，AS下创建AIDL需要在main目录下新建一个aidl文件夹，然后定义一个
和aidl包名相同的包，最后创建一个aidl文件，接着按ctrl + f9重新编译，就可以了！</p>

<p><img decoding="async" src="//www.runoob.com/wp-content/uploads/2015/08/10624316.jpg" alt="" /></p>

<p>上面两者成功编译的结果如下，你可以分别在对应目录下找到对应的AIDL文件</p>

<p><img decoding="async" src="//www.runoob.com/wp-content/uploads/2015/08/83128316.jpg" alt="" /> <img decoding="async" src="//www.runoob.com/wp-content/uploads/2015/08/25437793.jpg" alt="" /></p></blockquote>

<hr />

<p><strong>1.服务端：</strong></p>

<p><strong>Step 1：</strong>创建AIDL文件：</p>

<p><strong>IPerson.aidl</strong>
</p>
<pre>
package com.jay.aidl;

interface IPerson {
    String queryPerson(int num);
}
</pre>
<p><strong>我们打开IPerson.java看看里面的代码：</strong></p><p>
<strong>IPerson.java</strong></p>
<pre>
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: C:\\Code\\ASCode\\AIDLServer\\app\\src\\main\\aidl\\com\\jay\\aidl\\IPerson.aidl
 */
package com.jay.aidl;
public interface IPerson extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.jay.aidl.IPerson
{
private static final java.lang.String DESCRIPTOR = "com.jay.aidl.IPerson";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.jay.aidl.IPerson interface,
 * generating a proxy if needed.
 */
public static com.jay.aidl.IPerson asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&amp;&amp;(iin instanceof com.jay.aidl.IPerson))) {
return ((com.jay.aidl.IPerson)iin);
}
return new com.jay.aidl.IPerson.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_queryPerson:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.queryPerson(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.jay.aidl.IPerson
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public java.lang.String queryPerson(int num) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num);
mRemote.transact(Stub.TRANSACTION_queryPerson, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_queryPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.lang.String queryPerson(int num) throws android.os.RemoteException;
}
</pre><p>
这里我们关注的只是**asInterface(IBinder)**和我们定义的接口中的**queryPerson()**方法!</p><p>
该方法会把IBinder类型的对象转换成IPerson类型的,必要时生成一个代理对象返回结果！</p><p>
其他的我们可以不看，直接跳过，进行下一步。</p>


<p><b>Step 2：**自定义我们的Service类,完成下述操作:</b></p>
<p>
1)继承Service类,同时也自定义了一个PersonQueryBinder类用来<b>继承IPerson.Stub类</b>
就是<b>实现了IPerson接口和IBinder接口</b></p><p>
2)实例化自定义的Stub类,并重写Service的onBind方法,返回一个binder对象!
</p><p><b>AIDLService.java</b></p>
<pre>
package com.jay.aidlserver;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import com.jay.aidl.IPerson.Stub;

/**
 * Created by Jay on 2015/8/18 0018.
 */
public class AIDLService extends Service {

    private IBinder binder = new PersonQueryBinder();
    private String[] names = {"B神","艹神","基神","J神","翔神"};

    private String query(int num)
    {
        if(num &gt; 0 &amp;&amp; num &lt; 6){
            return names[num - 1];
        }
        return null;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private final class PersonQueryBinder extends Stub{
        @Override
        public String queryPerson(int num) throws RemoteException {
            return query(num);
        }
    }
}
</pre>

<p><strong>Step 3：</strong>在AndroidManifest.xml文件中注册Service
</p>
<pre>
&lt;service android:name=".AIDLService"&gt;
            &lt;intent-filter&gt;
                &lt;action android:name="android.intent.action.AIDLService" /&gt;
                &lt;category android:name="android.intent.category.DEFAULT" /&gt;
            &lt;/intent-filter&gt;
        &lt;/service&gt;
</pre>
<p>这里我们并没有提供Activity界面，但是改应用提供的Service可以供其他app来调用！</p>

<hr />

<p data-anchor-id="5gyv"><strong>2.客户端</strong> <br>
直接把服务端的那个aidl文件复制过来，然后我们直接在MainActivity中完成，和绑定本地Service的操作 <br>
有点类似，流程如下： <br>
1)自定义PersonConnection类<strong>实现ServiceConnection接口</strong> <br>
2)以PersonConnection对象作为参数,调用bindService绑定远程Service <br>
<strong>bindService(service,conn,BIND_AUTO_CREATE);</strong> <br>
ps:第三个参数是设置如果服务没有启动的话,自动创建 <br>
3)和本地Service不同，<strong>绑定远程Service的ServiceConnection并不能直接获取Service的onBind( )方法</strong> <br>
返回的IBinder对象，只能返回<strong>onBind( )</strong>方法所返回的<strong>代理对象</strong>，需要做如下处理: <br>
<strong>iPerson = IPerson.Stub.asInterface(service);</strong> <br>
再接着完成初始化,以及按钮事件等就可以了</p>

<p>具体代码如下：</p>

<p><strong>MainActivity.java</strong>
</p>
<pre>
package com.jay.aidlclient;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.jay.aidl.IPerson;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private EditText edit_num;
    private Button btn_query;
    private TextView txt_name;
    private IPerson iPerson;
    private PersonConnection conn = new PersonConnection();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindViews();
        //绑定远程Service
        Intent service = new Intent("android.intent.action.AIDLService");
        service.setPackage("com.jay.aidlserver");

        bindService(service, conn, BIND_AUTO_CREATE);
        btn_query.setOnClickListener(this);
    }

    private void bindViews() {
        edit_num = (EditText) findViewById(R.id.edit_num);
        btn_query = (Button) findViewById(R.id.btn_query);
        txt_name = (TextView) findViewById(R.id.txt_name);
    }

    @Override
    public void onClick(View v) {
        String number = edit_num.getText().toString();
        int num = Integer.valueOf(number);
        try {
            txt_name.setText(iPerson.queryPerson(num));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        edit_num.setText("");
    }

    private final class PersonConnection implements ServiceConnection {
        public void onServiceConnected(ComponentName name, IBinder service) {
            iPerson = IPerson.Stub.asInterface(service);
        }
        public void onServiceDisconnected(ComponentName name) {
            iPerson = null;
        }
    }
}
</pre>

<p>接下来先启动AIDLServivce，然后再启动AIDLClient，输入查询序号，即可获得对应姓名！
当然也可以直接启动AIDLClient，也会获得同样效果：</p>

<p><strong>效果图如下：</strong></p>

<p><img decoding="async" src="//www.runoob.com/wp-content/uploads/2015/08/16117222.jpg" alt="" /></p>

<hr />

<h3>3）传递复杂数据的AIDL Service</h3>

<blockquote><p>上面的例子我们传递的只是要给int类型的参数，然后服务端返回一个String类型的参数，看似满足
我们的基本需求，不过实际开发中，我们可能需要考虑传递复杂数据类型的情况！下面我们来学习下
如何向服务端传递复杂数据类型的数据！开始之前我们先来了解<strong>Parcelable接口</strong>！</p></blockquote>

<p><strong>——Parcelable接口简介：</strong></p>

<p>相信用过序列化的基本上都知道这个接口了，除了他还有另外一个Serializable，同样是用于序列化的，
只是Parcelable更加轻量级，速度更快！但是写起来就有点麻烦了，当然如果你用的as的话可以用
的插件来完成序列化，比如：<strong>Android Parcelable Code Generator</strong>
当然，这里我们还是手把手教大家来实现这个接口~</p>

<p><strong>首先</strong>需要实现：<strong>writeToParcel</strong>和<strong>readFromPacel</strong>方法
写入方法将对象写入到包裹(parcel)中,而读取方法则从包裹中读取对象,
请注意,写入属性顺序需与读取顺序相同</p>

<p><strong>接着</strong>需要在：该类中添加一个名为<strong>CREATOR</strong>的<strong>static final</strong>属性
改属性需要实现：android.os.Parcelable.Creator<T>接口</p>

<p><strong>再接着</strong>需要从写接口中的两个方法：
<strong>createFromParcel</strong>(Parcel source)方法:实现从source创建出JavaBean实例的功能
<strong>newArray</strong>(int size):创建一个类型为T,长度为size的数组,只有一个简单的return new T[size]; (这里的T是Person类)</p>

<p><strong>最后，describeContents()</strong>:这个我也不知道是拿来干嘛的,直接返回0即可！不用理他</p>

<p><strong>——另外</strong>，<strong>非原始类型中</strong>，除了<strong>String</strong>和<strong>CharSequence</strong>以外，其余均需要一个<strong>方向指示符</strong>。
方向指示符包括
<strong>in</strong>、<strong>out</strong>、<strong>和inout</strong>。in表示由客户端设置，out表示由服务端设置，inout表示客户端和服务端都设置了该值。</p>

<hr />

<p>好的，接着来写代码试试(AS这里自定义类型有点问题，暂时还没解决，就用回Eclipse~)：</p>

<p><strong>代码示例：</strong></p>

<p>自定义两种对象类型:Person与Salary,Person作为调用远程的Service的参数,Salary作为返回值!
那么首先要做的就是创建Person与Salary类,同时需要实现Parcelable接口</p>

<p><strong>1.——服务端</strong></p>

<p><strong>Step 1</strong>：创建Person.aidl和Salary.aidl的文件，因为他们需要实现Parcelable接口，所以就下面一条语句：
</p>
<pre>
Person.aidl:     parcelable Person; 
Salary.aidl:     parcelable Salary; 
</pre>
<strong>Step 2</strong>：分别建立Person类与Salary类，需实现Parcelable接口，重写对应的方法!</p>

<blockquote><p>PS:因为我们后面是根据Person对象来获取Map集合中的数据,所以Person.java中我们重写了hashcode和equals
的方法;而Salary类则不需要!</p></blockquote>

<p><strong>Person.java:</strong>
</p>
<pre>
package com.jay.example.aidl; 

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by Jay on 2015/8/18 0018.
 */
public class Person implements Parcelable{

    private Integer id;
    private String name;

    public Person() {}

    public Person(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }


    //实现Parcelable必须实现的方法,不知道拿来干嘛的,直接返回0就行了
    @Override
    public int describeContents() {
        return 0;
    }


    //写入数据到Parcel中的方法
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        //把对象所包含的数据写入到parcel中
        dest.writeInt(id);
        dest.writeString(name);
    }

    //必须提供一个名为CREATOR的static final属性 该属性需要实现
    //android.os.Parcelable.Creator&lt;T&gt;接口
    public static final Parcelable.Creator&lt;Person&gt; CREATOR = new Parcelable.Creator&lt;Person&gt;() {
        //从Parcel中读取数据,返回Person对象
        @Override
        public Person createFromParcel(Parcel source) {
            return new Person(source.readInt(),source.readString());
        }
        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };

    //因为我们集合取出元素的时候是根据Person对象来取得,所以比较麻烦,
    //需要我们重写hashCode()和equals()方法
    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Person other = (Person) obj;
        if (name == null)
        {
            if (other.name != null)
                return false;
        }
        else if (!name.equals(other.name))
            return false;
        return true;
    }
}
&lt;pre&gt;&lt;p&gt;&lt;strong&gt;Salary.java&lt;/strong&gt;~照葫芦画瓢&lt;/p&gt;

&lt;pre&gt;
package com.jay.example.aidl; 

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by Jay on 2015/8/18 0018.
 */
public class Salary implements Parcelable {

    private String type;
    private Integer salary;

    public Salary() {
    }

    public Salary(String type, Integer salary) {
        this.type = type;
        this.salary = salary;
    }

    public String getType() {
        return type;
    }

    public Integer getSalary() {
        return salary;
    }

    public void setType(String type) {
        this.type = type;
    }

    public void setSalary(Integer salary) {
        this.salary = salary;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(type);
        dest.writeInt(salary);
    }

    public static final Parcelable.Creator&lt;Salary&gt; CREATOR = new Parcelable.Creator&lt;Salary&gt;() {
        //从Parcel中读取数据,返回Person对象
        @Override
        public Salary createFromParcel(Parcel source) {
            return new Salary(source.readString(), source.readInt());
        }

        @Override
        public Salary[] newArray(int size) {
            return new Salary[size];
        }
    };

    public String toString() {
        return "工作:" + type + "    薪水: " + salary;
    }
}
</pre>

<p><strong>Step 3</strong>：创建一个ISalary.aidl的文件，在里面写一个简单的获取工资信息的方法：
</p>
<pre>
package com.jay.example.aidl;
  
import com.jay.example.aidl.Salary;  
import com.jay.example.aidl.Person;  
interface ISalary  
{  
    //定义一个Person对象作为传入参数  
    //接口中定义方法时,需要制定新参的传递模式,这里是传入,所以前面有一个in  
    Salary getMsg(in Person owner);  
}  
</pre>

<p><strong>ps:</strong>这里可以记得如果使用的是自定义的数据类型的话,需要import哦！！！切记！！！</p>

<p><strong>Step 4：</strong>核心Service的编写：
定义一个SalaryBinder类继承Stub,从而实现ISalary和IBinder接口;定义一个存储信息的Map集合!
重新onBind方法,返回SalaryBinder类的对象实例!</p>

<p><strong>AidlService.java</strong></p>

<pre>
package com.jay.example.aidl_complexservice;  
  
import java.util.HashMap;  
import java.util.Map;  
import com.jay.example.aidl.ISalary.Stub;  
import com.jay.example.aidl.Person;  
import com.jay.example.aidl.Salary;  
import android.app.Service;  
import android.content.Intent;  
import android.os.IBinder;  
import android.os.RemoteException;  
  
public class AidlService extends Service {  
  
    private SalaryBinder salaryBinder;  
    private static Map&lt;Person,Salary&gt; ss = new HashMap&lt;Person, Salary&gt;();  
    //初始化Map集合,这里在静态代码块中进行初始化,当然你可也以在构造方法中完成初始化  
    static  
    {  
        ss.put(new Person(1, "Jay"), new Salary("码农", 2000));  
        ss.put(new Person(2, "GEM"), new Salary("歌手", 20000));  
        ss.put(new Person(3, "XM"), new Salary("学生", 20));  
        ss.put(new Person(4, "MrWang"), new Salary("老师", 2000));  
    }  
      
      
    @Override  
    public void onCreate() {  
        super.onCreate();  
        salaryBinder = new SalaryBinder();  
    }  
      
    @Override  
    public IBinder onBind(Intent intent) {  
        return salaryBinder;  
    }  
  
      
    //同样是继承Stub,即同时实现ISalary接口和IBinder接口  
    public class SalaryBinder extends Stub  
    {  
        @Override  
        public Salary getMsg(Person owner) throws RemoteException {  
            return ss.get(owner);  
        }  
    }  
      
    @Override  
    public void onDestroy() {  
        System.out.println("服务结束！");  
        super.onDestroy();  
    }  
}  
</pre>

<p><strong>注册下Service:</strong></p>
<pre>
&lt;service android:name=".AidlService"&gt;  
    &lt;intent-filter&gt;    
        &lt;action android:name="android.intent.action.AIDLService" /&gt;  
        &lt;category android:name="android.intent.category.DEFAULT" /&gt;  
    &lt;/intent-filter&gt;    
&lt;/service&gt;
</pre>

<hr />

<p><strong>2——客户端编写</strong></p>

<p><strong>Step 1：</strong>把服务端的AIDL文件拷贝下，拷贝后目录如下：</p>

<p><img decoding="async" src="//www.runoob.com/wp-content/uploads/2015/08/39100068.jpg" alt="" /></p>

<p><strong>Step 2</strong>：编写简单的布局,再接着就是核心MainActvitiy的实现了
定义一个ServciceConnection对象,重写对应方法,和前面的普通数据的类似
再接着在bindService,然后再Button的点击事件中获取Salary对象并显示出来！</p>

<p><strong>MainActivity.java</strong></p>
<pre>package com.jay.example.aidl_complexclient;  
  
import com.jay.example.aidl.ISalary;  
import com.jay.example.aidl.Person;  
import com.jay.example.aidl.Salary;  
  
import android.app.Activity;  
import android.app.Service;  
import android.content.ComponentName;  
import android.content.Intent;  
import android.content.ServiceConnection;  
import android.os.Bundle;  
import android.os.IBinder;  
import android.os.RemoteException;  
import android.view.View;  
import android.view.View.OnClickListener;  
import android.widget.Button;  
import android.widget.EditText;  
import android.widget.TextView;  
  
  
public class MainActivity extends Activity {  
  
    private ISalary salaryService;  
    private Button btnquery;  
    private EditText editname;  
    private TextView textshow;  
    private ServiceConnection conn = new ServiceConnection() {  
          
        @Override  
        public void onServiceDisconnected(ComponentName name) {  
            salaryService = null;  
        }  
          
        @Override  
        public void onServiceConnected(ComponentName name, IBinder service) {  
            //返回的是代理对象,要调用这个方法哦!  
            salaryService = ISalary.Stub.asInterface(service);  
        }  
    };  
      
      
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
          
        btnquery = (Button) findViewById(R.id.btnquery);  
        editname = (EditText) findViewById(R.id.editname);  
        textshow = (TextView) findViewById(R.id.textshow);  
          
        Intent it = new Intent();  
        it.setAction("com.jay.aidl.AIDL_SERVICE");  
        bindService(it, conn, Service.BIND_AUTO_CREATE);  
          
        btnquery.setOnClickListener(new OnClickListener() {           
            @Override  
            public void onClick(View v) {  
                try  
                {  
                    String name = editname.getText().toString();  
                    Salary salary = salaryService.getMsg(new Person(1,name));  
                    textshow.setText(name + salary.toString());  
                }catch(RemoteException e){e.printStackTrace();}  
            }  
        });  
          
    }  
    @Override  
    protected void onDestroy() {  
        super.onDestroy();  
        this.unbindService(conn);  
    }  
      
} 
</pre>

<p><strong>运行截图：</strong></p>

<p><img decoding="async" src="//www.runoob.com/wp-content/uploads/2015/08/20219303.jpg" alt="" /></p>

<p><strong>PS：</strong> 这里的代码是之前用Eclipse写的代码，Android Studio下自定义类型有点问题，
暂时没找到解决方法，如果知道的朋友请告知下！！！万分感激！！！
出现的问题如下：
<img decoding="async" src="//www.runoob.com/wp-content/uploads/2015/08/96099606.jpg" alt="" /></p>

<p>两个实例的代码下载(基于Eclipse的)：<br>
1)<a href="../try/download/AIDL-sample-demo.zip">使用AIDL完成进程间的简单通信</a><br>
2）<a href="../try/download/complex-data-AIDL.zip">传递复杂数据的AIDL Service的实现</a></p>

<hr />

<h2>3.直接通过Binder的onTransact完成跨进程通信</h2>

<p>上面讲过Android可以通过Binder的onTrensact方法来完成通信，下面就来简单试下下，还是前面那个根据
序号查询名字的例子：</p>

<p><strong>服务端实现</strong>：
</p>
<pre>
/**
 * Created by Jay on 2015/8/18 0018.
 */
public class IPCService extends Service{

    private static final String DESCRIPTOR = "IPCService";
    private final String[] names = {"B神","艹神","基神","J神","翔神"};
    private MyBinder mBinder = new MyBinder();


    private class MyBinder extends Binder {
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            switch (code){
                case 0x001: {
                    data.enforceInterface(DESCRIPTOR);
                    int num = data.readInt();
                    reply.writeNoException();
                    reply.writeString(names[num]);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}
</pre>
<p><strong>客户端实现</strong>：</p>

<pre>
public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private EditText edit_num;
    private Button btn_query;
    private TextView txt_result;
    private IBinder mIBinder;
    private ServiceConnection PersonConnection  = new ServiceConnection()
    {
        @Override
        public void onServiceDisconnected(ComponentName name)
        {
            mIBinder = null;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service)
        {
            mIBinder = service;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindViews();

        //绑定远程Service
        Intent service = new Intent("android.intent.action.IPCService");
        service.setPackage("com.jay.ipcserver");
        bindService(service, PersonConnection, BIND_AUTO_CREATE);
        btn_query.setOnClickListener(this);
    }

    private void bindViews() {
        edit_num = (EditText) findViewById(R.id.edit_num);
        btn_query = (Button) findViewById(R.id.btn_query);
        txt_result = (TextView) findViewById(R.id.txt_result);
    }

    @Override
    public void onClick(View v) {
        int num = Integer.parseInt(edit_num.getText().toString());
        if (mIBinder == null)
        {
            Toast.makeText(this, "未连接服务端或服务端被异常杀死", Toast.LENGTH_SHORT).show();
        } else {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            String _result = null;
            try{
                _data.writeInterfaceToken("IPCService");
                _data.writeInt(num);
                mIBinder.transact(0x001, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readString();
                txt_result.setText(_result);
                edit_num.setText("");
            }catch (RemoteException e)
            {
                e.printStackTrace();
            } finally
            {
                _reply.recycle();
                _data.recycle();
            }
        }
    }
}
</pre>

<p><strong>运行截图：</strong></p>

<p><img decoding="async" src="//www.runoob.com/wp-content/uploads/2015/08/33184779.jpg" alt="" /></p>

<p>代码比较简单，就不多解释了~用到自己改改即可！
<strong>PS</strong>:代码参考于:<a href="http://blog.csdn.net/lmj623565791/article/details/38461079#t9">Android aidl Binder框架浅析</a></p>

<hr />

<h2>4.Android 5.0后Service一些要注意的地方：</h2>

<blockquote><p>今天在隐式启动Service的时候，遇到这样一个问题
<img decoding="async" src="//www.runoob.com/wp-content/uploads/2015/08/3192647.jpg" alt="" />
然后程序一启动就崩了,后来苦扣良久才发下是Android 5.0惹的祸，
原来5.0后有个新的特性，就是：
<strong>Service Intent  must be explitict！</strong>
好吧，就是不能隐式去启动Service咯，解决的方法也很简单！
比如StartService的：</p>

<p><strong>startService(new Intent(getApplicationContext(), "com.aaa.xxxserver"));</strong>
这样写程序直接crash掉，要写成下面这样：
<strong>startService(new Intent(getApplicationContext(), LoadContactsService.class));</strong></p>

<p>如果是BindService的：
<strong>Intent service = new Intent("android.intent.action.AIDLService");</strong>
的基础上，要加上包名：
<strong>service.setPackage("com.jay.ipcserver");</strong>
这样就可以了~</p></blockquote>

<p>官方文档：<a href="http://developer.android.com/intl/zh-cn/guide/components/intents-filters.html#Types">http://developer.android.com/intl/zh-cn/guide/components/intents-filters.html#Types</a>
文档说明处:<img decoding="async" src="//www.runoob.com/wp-content/uploads/2015/08/12090445.jpg" alt="" /></p>

<hr />

<h2>本节小结：</h2>

<blockquote><p>好的，关于Service的最后一节就到这里，本节讲解了Binder的基本概念以及实现进程间通信的
两种方式：通过AIDL以及Binder.onTransact()来实现跨进程通信！最后还讲解了下Android 5.0后
使用Service不能隐式启动的注意事项！就到这里，谢谢~</p></blockquote>				</div>
			</div>
			<div class="previous-next-links">
			<div class="previous-design-link">← <a href="../w3cnote/android-tutorial-service-2.html" rel="prev"> 4.2.2 Service进阶</a> </div>
			<div class="next-design-link"><a href="../w3cnote/android-tutorial-seekbar.html" rel="next"> 2.3.8 SeekBar(拖动条)</a> →</div>
			</div>
						<div class="article-heading-ad" id="w3cnote-ad728">
			<script async src=""></script>
			<!-- 移动版 自动调整 -->
			<ins class="adsbygoogle"
			     style="display:inline-block;min-width:300px;max-width:970px;width:100%;height:90px"
			     data-ad-client="ca-pub-5751451760833794"
			     data-ad-slot="1691338467"
			     data-ad-format="horizontal"></ins>
			<script>
			(adsbygoogle = window.adsbygoogle || []).push({});
			</script>
			</div>
			<style>
@media screen and (max-width: 768px) {
	#w3cnote-ad728 {
		display: none;
	}
}
p.note-author {
    border-bottom: 1px solid #ddd;
    font-size: 18px;
    font-weight: bold;
    color: #78a15a;
    padding-bottom: 2px;
    margin-bottom: 4px;
}
</style>
<script>
var aid = 13237;
</script>
	</div>
		
	</div>
	<div class="listcol last right-column">

	<script type="text/javascript">
jQuery(document).ready(function ($){
	var total = $(".membership li").length;
	var left_list = '';
	href = window.location.href;
	
	$(".membership li").each(function(index, value){

		left_list += $(this).html();

		cur_href = $(this).find("a").attr("href");
		cur_obj = $(this);
		
		if(href.match(cur_href) != null) {
			if(index==0) {
				$(".previous-design-link").hide();
			}
			if(index==(total-1)) {
				$(".next-design-link").hide();
			}
			prev_href = cur_obj.prev("li").find("a").attr("href");
			prev_title = cur_obj.prev("li").find("a").attr("title");

			next_href = cur_obj.next("li").find("a").attr("href");
			next_title = cur_obj.next("li").find("a").attr("title");
			if(prev_title) {
				$(".previous-design-link a").attr("href", prev_href);
				$(".previous-design-link a").attr("title", prev_title);
				$(".previous-design-link a").text(prev_title);
			} else {
				$(".previous-design-link").html("");
			}

			if(next_title) {
				$(".next-design-link a").attr("href", next_href);
				$(".next-design-link a").attr("title", next_title);
				$(".next-design-link a").text(next_title);
			} else {
				$(".next-design-link").html("");
			}
		}
	});

	$("#leftcolumn").html(left_list);
});
	</script>
	<div class="sidebar-box cate-list">
	<div class="sidebar-box recommend-here list-link">
	<a href="javascript:void(0);">Android 基础入门教程</a>	</div>
	
	<ul class="membership">
		
	<li><a target="_top" data-id="12252" title="1.0 Android基础入门教程" href="../w3cnote/android-tutorial-intro.html" >1.0 Android基础入门教程</a></li>
	
		
	<li><a target="_top" data-id="14860" title="1.0.1 2015年最新Android基础入门教程目录" href="../w3cnote/android-tutorial-contents.html" >1.0.1 2015年最新Android基础入门教程目录</a></li>
	
		
	<li><a target="_top" data-id="12255" title="1.1 背景相关与系统架构分析" href="../w3cnote/android-tutorial-system-architecture-analysis.html" >1.1 背景相关与系统架构分析</a></li>
	
		
	<li><a target="_top" data-id="12259" title="1.2 开发环境搭建" href="../w3cnote/android-tutorial-development-environment-build.html" >1.2 开发环境搭建</a></li>
	
		
	<li><a target="_top" data-id="12270" title="1.2.1 使用Eclipse + ADT + SDK开发Android APP" href="../w3cnote/android-tutorial-eclipse-adt-sdk-app.html" >1.2.1 使用Eclipse + ADT + SDK开发Android APP</a></li>
	
		
	<li><a target="_top" data-id="12280" title="1.2.2 使用Android Studio开发Android APP" href="../w3cnote/android-tutorial-android-studio.html" >1.2.2 使用Android Studio开发Android APP</a></li>
	
		
	<li><a target="_top" data-id="12293" title="1.3 SDK更新不了问题解决" href="../w3cnote/android-tutorial-sdk-problem-solve.html" >1.3 SDK更新不了问题解决</a></li>
	
		
	<li><a target="_top" data-id="12329" title="1.4 Genymotion模拟器安装" href="../w3cnote/android-tutorial-genymotion-install.html" >1.4 Genymotion模拟器安装</a></li>
	
		
	<li><a target="_top" data-id="12331" title="1.5.1 Git使用教程之本地仓库的基本操作" href="../w3cnote/android-tutorial-git-repo-operate.html" >1.5.1 Git使用教程之本地仓库的基本操作</a></li>
	
		
	<li><a target="_top" data-id="12366" title="1.5.2 Git之使用GitHub搭建远程仓库" href="../w3cnote/android-tutorial-git-repo-create.html" >1.5.2 Git之使用GitHub搭建远程仓库</a></li>
	
		
	<li><a target="_top" data-id="12394" title="1.6  9(九妹)图片怎么玩" href="../w3cnote/android-tutorial-9-image.html" >1.6  9(九妹)图片怎么玩</a></li>
	
		
	<li><a target="_top" data-id="12478" title="1.7 界面原型设计" href="../w3cnote/android-tutorial-interface-design.html" >1.7 界面原型设计</a></li>
	
		
	<li><a target="_top" data-id="12479" title="1.8 工程相关解析(各种文件，资源访问)" href="../w3cnote/android-tutorial-project-src-analysis.html" >1.8 工程相关解析(各种文件，资源访问)</a></li>
	
		
	<li><a target="_top" data-id="12507" title="1.9 Android程序签名打包" href="../w3cnote/android-tutorial-sign-package.html" >1.9 Android程序签名打包</a></li>
	
		
	<li><a target="_top" data-id="12523" title="1.11 反编译APK获取代码&资源" href="../w3cnote/android-tutorial-decompile-apk-get-code-resources.html" >1.11 反编译APK获取代码&资源</a></li>
	
		
	<li><a target="_top" data-id="12539" title="2.1 View与ViewGroup的概念" href="../w3cnote/android-tutorial-view-viewgroup-intro.html" >2.1 View与ViewGroup的概念</a></li>
	
		
	<li><a target="_top" data-id="12555" title="2.2.1 LinearLayout(线性布局)" href="../w3cnote/android-tutorial-linearlayout.html" >2.2.1 LinearLayout(线性布局)</a></li>
	
		
	<li><a target="_top" data-id="12569" title="2.2.2 RelativeLayout(相对布局)" href="../w3cnote/android-tutorial-relativelayout.html" >2.2.2 RelativeLayout(相对布局)</a></li>
	
		
	<li><a target="_top" data-id="12577" title="2.2.3 TableLayout(表格布局)" href="../w3cnote/android-tutorial-tablelayout.html" >2.2.3 TableLayout(表格布局)</a></li>
	
		
	<li><a target="_top" data-id="12582" title="2.2.4 FrameLayout(帧布局)" href="../w3cnote/android-tutorial-framelayout.html" >2.2.4 FrameLayout(帧布局)</a></li>
	
		
	<li><a target="_top" data-id="12587" title="2.2.5 GridLayout(网格布局)" href="../w3cnote/android-tutorial-gridlayout.html" >2.2.5 GridLayout(网格布局)</a></li>
	
		
	<li><a target="_top" data-id="12591" title="2.2.6 AbsoluteLayout(绝对布局)" href="../w3cnote/android-tutorial-absolutelayout.html" >2.2.6 AbsoluteLayout(绝对布局)</a></li>
	
		
	<li><a target="_top" data-id="12624" title="2.3.1 TextView(文本框)详解" href="../w3cnote/android-tutorial-textview.html" >2.3.1 TextView(文本框)详解</a></li>
	
		
	<li><a target="_top" data-id="12701" title="2.3.2 EditText(输入框)详解" href="../w3cnote/android-tutorial-edittext.html" >2.3.2 EditText(输入框)详解</a></li>
	
		
	<li><a target="_top" data-id="12745" title="2.3.3 Button(按钮)与ImageButton(图像按钮)" href="../w3cnote/android-tutorial-button-imagebutton.html" >2.3.3 Button(按钮)与ImageButton(图像按钮)</a></li>
	
		
	<li><a target="_top" data-id="12770" title="2.3.4 ImageView(图像视图)" href="../w3cnote/android-tutorial-imageview.html" >2.3.4 ImageView(图像视图)</a></li>
	
		
	<li><a target="_top" data-id="12790" title="2.3.5.RadioButton(单选按钮)&Checkbox(复选框)" href="../w3cnote/android-tutorial-radiobutton-checkbox.html" >2.3.5.RadioButton(单选按钮)&Checkbox(复选框)</a></li>
	
		
	<li><a target="_top" data-id="12829" title="2.3.6 开关按钮ToggleButton和开关Switch" href="../w3cnote/android-tutorial-togglebutton-switch.html" >2.3.6 开关按钮ToggleButton和开关Switch</a></li>
	
		
	<li><a target="_top" data-id="13133" title="2.3.7 ProgressBar(进度条)" href="../w3cnote/android-tutorial-progressbar.html" >2.3.7 ProgressBar(进度条)</a></li>
	
		
	<li><a target="_top" data-id="13258" title="2.3.8 SeekBar(拖动条)" href="../w3cnote/android-tutorial-seekbar.html" >2.3.8 SeekBar(拖动条)</a></li>
	
		
	<li><a target="_top" data-id="13267" title="2.3.9 RatingBar(星级评分条)" href="../w3cnote/android-tutorial-ratingbar.html" >2.3.9 RatingBar(星级评分条)</a></li>
	
		
	<li><a target="_top" data-id="13293" title="2.4.1 ScrollView(滚动条)" href="../w3cnote/android-tutorial-scrollview.html" >2.4.1 ScrollView(滚动条)</a></li>
	
		
	<li><a target="_top" data-id="13357" title="2.4.2 Date & Time组件(上)" href="../w3cnote/android-tutorial-date-time-1.html" >2.4.2 Date & Time组件(上)</a></li>
	
		
	<li><a target="_top" data-id="13364" title="2.4.3 Date & Time组件(下)" href="../w3cnote/android-tutorial-date-time-2.html" >2.4.3 Date & Time组件(下)</a></li>
	
		
	<li><a target="_top" data-id="13477" title="2.4.4 Adapter基础讲解" href="../w3cnote/android-tutorial-adapter.html" >2.4.4 Adapter基础讲解</a></li>
	
		
	<li><a target="_top" data-id="13784" title="2.4.5 ListView简单实用" href="../w3cnote/android-tutorial-listview.html" >2.4.5 ListView简单实用</a></li>
	
		
	<li><a target="_top" data-id="13807" title="2.4.6 BaseAdapter优化" href="../w3cnote/android-tutorial-baseadapter.html" >2.4.6 BaseAdapter优化</a></li>
	
		
	<li><a target="_top" data-id="13808" title="2.4.7ListView的焦点问题" href="../w3cnote/android-tutorial-listview-focus.html" >2.4.7ListView的焦点问题</a></li>
	
		
	<li><a target="_top" data-id="13809" title="2.4.8 ListView之checkbox错位问题解决" href="../w3cnote/android-tutorial-listview-checkbox.html" >2.4.8 ListView之checkbox错位问题解决</a></li>
	
		
	<li><a target="_top" data-id="13812" title="2.4.9 ListView的数据更新问题" href="../w3cnote/android-tutorial-listview-update.html" >2.4.9 ListView的数据更新问题</a></li>
	
		
	<li><a target="_top" data-id="13823" title="2.5.0 构建一个可复用的自定义BaseAdapter" href="../w3cnote/android-tutorial-customer-baseadapter.html" >2.5.0 构建一个可复用的自定义BaseAdapter</a></li>
	
		
	<li><a target="_top" data-id="13839" title="2.5.1 ListView Item多布局的实现" href="../w3cnote/android-tutorial-listview-item.html" >2.5.1 ListView Item多布局的实现</a></li>
	
		
	<li><a target="_top" data-id="13885" title="2.5.2 GridView(网格视图)的基本使用" href="../w3cnote/android-tutorial-gridview.html" >2.5.2 GridView(网格视图)的基本使用</a></li>
	
		
	<li><a target="_top" data-id="13897" title="2.5.3 Spinner(列表选项框)的基本使用" href="../w3cnote/android-tutorial-spinner.html" >2.5.3 Spinner(列表选项框)的基本使用</a></li>
	
		
	<li><a target="_top" data-id="13900" title="2.5.4 AutoCompleteTextView(自动完成文本框)的基本使用" href="../w3cnote/android-tutorial-autocompletetextview.html" >2.5.4 AutoCompleteTextView(自动完成文本框)的基本使用</a></li>
	
		
	<li><a target="_top" data-id="13933" title="2.5.5 ExpandableListView(可折叠列表)的基本使用" href="../w3cnote/android-tutorial-expandablelistview.html" >2.5.5 ExpandableListView(可折叠列表)的基本使用</a></li>
	
		
	<li><a target="_top" data-id="13936" title="2.5.6 ViewFlipper(翻转视图)的基本使用" href="../w3cnote/android-tutorial-viewflipper.html" >2.5.6 ViewFlipper(翻转视图)的基本使用</a></li>
	
		
	<li><a target="_top" data-id="13952" title="2.5.7 Toast(吐司)的基本使用" href="../w3cnote/android-tutorial-toast.html" >2.5.7 Toast(吐司)的基本使用</a></li>
	
		
	<li><a target="_top" data-id="13957" title="2.5.8 Notification(状态栏通知)详解" href="../w3cnote/android-tutorial-notification.html" >2.5.8 Notification(状态栏通知)详解</a></li>
	
		
	<li><a target="_top" data-id="13963" title="2.5.9 AlertDialog(对话框)详解" href="../w3cnote/android-tutorial-alertdialog.html" >2.5.9 AlertDialog(对话框)详解</a></li>
	
		
	<li><a target="_top" data-id="13981" title="2.6.0 其他几种常用对话框基本使用" href="../w3cnote/android-tutorial-dialog.html" >2.6.0 其他几种常用对话框基本使用</a></li>
	
		
	<li><a target="_top" data-id="13984" title="2.6.1 PopupWindow(悬浮框)的基本使用" href="../w3cnote/android-tutorial-popupwindow.html" >2.6.1 PopupWindow(悬浮框)的基本使用</a></li>
	
		
	<li><a target="_top" data-id="13986" title="2.6.2 菜单(Menu)" href="../w3cnote/android-tutorial-menu.html" >2.6.2 菜单(Menu)</a></li>
	
		
	<li><a target="_top" data-id="14001" title="2.6.3 ViewPager的简单使用" href="../w3cnote/android-tutorial-viewpager.html" >2.6.3 ViewPager的简单使用</a></li>
	
		
	<li><a target="_top" data-id="14007" title="2.6.4 DrawerLayout(官方侧滑菜单)的简单使用" href="../w3cnote/android-tutorial-drawerlayout.html" >2.6.4 DrawerLayout(官方侧滑菜单)的简单使用</a></li>
	
		
	<li><a target="_top" data-id="12714" title="3.1.1 基于监听的事件处理机制" href="../w3cnote/android-tutorial-listen-event-handle.html" >3.1.1 基于监听的事件处理机制</a></li>
	
		
	<li><a target="_top" data-id="12721" title="3.2 基于回调的事件处理机制" href="../w3cnote/android-tutorial-callback-event-handle.html" >3.2 基于回调的事件处理机制</a></li>
	
		
	<li><a target="_top" data-id="12715" title="3.3 Handler消息传递机制浅析" href="../w3cnote/android-tutorial-handler-message.html" >3.3 Handler消息传递机制浅析</a></li>
	
		
	<li><a target="_top" data-id="12730" title="3.4 TouchListener PK OnTouchEvent + 多点触碰" href="../w3cnote/android-tutorial-touchlistener-ontouchevent.html" >3.4 TouchListener PK OnTouchEvent + 多点触碰</a></li>
	
		
	<li><a target="_top" data-id="12749" title="3.5 监听EditText的内容变化" href="../w3cnote/android-tutorial-listener-edittext-change.html" >3.5 监听EditText的内容变化</a></li>
	
		
	<li><a target="_top" data-id="12752" title="3.6 响应系统设置的事件(Configuration类)" href="../w3cnote/android-tutorial-configuration.html" >3.6 响应系统设置的事件(Configuration类)</a></li>
	
		
	<li><a target="_top" data-id="12807" title="3.7 AsyncTask异步任务" href="../w3cnote/android-tutorial-ansynctask.html" >3.7 AsyncTask异步任务</a></li>
	
		
	<li><a target="_top" data-id="12915" title="3.8 Gestures(手势)" href="../w3cnote/android-tutorial-gestures.html" >3.8 Gestures(手势)</a></li>
	
		
	<li><a target="_top" data-id="13148" title="4.1.1 Activity初学乍练" href="../w3cnote/android-tutorial-activity.html" >4.1.1 Activity初学乍练</a></li>
	
		
	<li><a target="_top" data-id="13160" title="4.1.2 Activity初窥门径" href="../w3cnote/android-tutorial-activity-start.html" >4.1.2 Activity初窥门径</a></li>
	
		
	<li><a target="_top" data-id="13168" title="4.1.3 Activity登堂入室" href="../w3cnote/android-tutorial-activity-intro.html" >4.1.3 Activity登堂入室</a></li>
	
		
	<li><a target="_top" data-id="13198" title="4.2.1 Service初涉" href="../w3cnote/android-tutorial-service-1.html" >4.2.1 Service初涉</a></li>
	
		
	<li><a target="_top" data-id="13212" title="4.2.2 Service进阶" href="../w3cnote/android-tutorial-service-2.html" >4.2.2 Service进阶</a></li>
	
		<li>
	4.2.3 Service精通	</li>
	
		
	<li><a target="_top" data-id="13273" title="4.3.1 BroadcastReceiver牛刀小试" href="../w3cnote/android-tutorial-broadcastreceiver.html" >4.3.1 BroadcastReceiver牛刀小试</a></li>
	
		
	<li><a target="_top" data-id="13284" title="4.3.2 BroadcastReceiver庖丁解牛" href="../w3cnote/android-tutorial-broadcastreceiver-2.html" >4.3.2 BroadcastReceiver庖丁解牛</a></li>
	
		
	<li><a target="_top" data-id="13297" title="4.4.1 ContentProvider初探" href="../w3cnote/android-tutorial-contentprovider.html" >4.4.1 ContentProvider初探</a></li>
	
		
	<li><a target="_top" data-id="13319" title="4.4.2 ContentProvider再探——Document Provider" href="../w3cnote/android-tutorial-contentprovider-2.html" >4.4.2 ContentProvider再探——Document Provider</a></li>
	
		
	<li><a target="_top" data-id="13329" title="4.5.1 Intent的基本使用" href="../w3cnote/android-tutorial-intent-base.html" >4.5.1 Intent的基本使用</a></li>
	
		
	<li><a target="_top" data-id="13346" title="4.5.2 Intent之复杂数据的传递" href="../w3cnote/android-tutorial-intent-pass-data.html" >4.5.2 Intent之复杂数据的传递</a></li>
	
		
	<li><a target="_top" data-id="13401" title="5.1 Fragment基本概述" href="../w3cnote/android-tutorial-fragment-base.html" >5.1 Fragment基本概述</a></li>
	
		
	<li><a target="_top" data-id="13431" title="5.2.1 Fragment实例精讲——底部导航栏的实现(方法1)" href="../w3cnote/android-tutorial-fragment-demo1.html" >5.2.1 Fragment实例精讲——底部导航栏的实现(方法1)</a></li>
	
		
	<li><a target="_top" data-id="13432" title="5.2.2 Fragment实例精讲——底部导航栏的实现(方法2)" href="../w3cnote/android-tutorial-fragment-demo2.html" >5.2.2 Fragment实例精讲——底部导航栏的实现(方法2)</a></li>
	
		
	<li><a target="_top" data-id="13439" title="5.2.3 Fragment实例精讲——底部导航栏的实现(方法3)" href="../w3cnote/android-tutorial-fragment-demo3.html" >5.2.3 Fragment实例精讲——底部导航栏的实现(方法3)</a></li>
	
		
	<li><a target="_top" data-id="13448" title="5.2.4 Fragment实例精讲——底部导航栏+ViewPager滑动切换页面" href="../w3cnote/android-tutorial-fragment-demo4.html" >5.2.4 Fragment实例精讲——底部导航栏+ViewPager滑动切换页面</a></li>
	
		
	<li><a target="_top" data-id="13597" title="5.2.5 Fragment实例精讲——新闻(购物)类App列表Fragment的简单实现" href="../w3cnote/android-tutorial-fragment-demo5.html" >5.2.5 Fragment实例精讲——新闻(购物)类App列表Fragment的简单实现</a></li>
	
		
	<li><a target="_top" data-id="13478" title="6.1 数据存储与访问之——文件存储读写" href="../w3cnote/android-tutorial-file.html" >6.1 数据存储与访问之——文件存储读写</a></li>
	
		
	<li><a target="_top" data-id="13523" title="6.2 数据存储与访问之——SharedPreferences保存用户偏好参数" href="../w3cnote/android-tutorial-sharedpreferences.html" >6.2 数据存储与访问之——SharedPreferences保存用户偏好参数</a></li>
	
		
	<li><a target="_top" data-id="13574" title="6.3.1 数据存储与访问之——初见SQLite数据库" href="../w3cnote/android-tutorial-sqlite-intro.html" >6.3.1 数据存储与访问之——初见SQLite数据库</a></li>
	
		
	<li><a target="_top" data-id="13590" title="6.3.2 数据存储与访问之——又见SQLite数据库" href="../w3cnote/android-tutorial-sqlite-2.html" >6.3.2 数据存储与访问之——又见SQLite数据库</a></li>
	
		
	<li><a target="_top" data-id="13601" title="7.1.1 Android网络编程要学的东西与Http协议学习" href="../w3cnote/android-tutorial-http.html" >7.1.1 Android网络编程要学的东西与Http协议学习</a></li>
	
		
	<li><a target="_top" data-id="13607" title="7.1.2 Android Http请求头与响应头的学习" href="../w3cnote/android-tutorial-http-response-header.html" >7.1.2 Android Http请求头与响应头的学习</a></li>
	
		
	<li><a target="_top" data-id="13620" title="7.1.3 Android HTTP请求方式:HttpURLConnection" href="../w3cnote/android-tutorial-httpurlconnection.html" >7.1.3 Android HTTP请求方式:HttpURLConnection</a></li>
	
		
	<li><a target="_top" data-id="13631" title="7.1.4 Android HTTP请求方式:HttpClient" href="../w3cnote/android-tutorial-httpclient.html" >7.1.4 Android HTTP请求方式:HttpClient</a></li>
	
		
	<li><a target="_top" data-id="13633" title="7.2.1 Android XML数据解析" href="../w3cnote/android-tutorial-xml.html" >7.2.1 Android XML数据解析</a></li>
	
		
	<li><a target="_top" data-id="13681" title="7.2.2 Android JSON数据解析" href="../w3cnote/android-tutorial-json.html" >7.2.2 Android JSON数据解析</a></li>
	
		
	<li><a target="_top" data-id="13685" title="7.3.1 Android 文件上传" href="../w3cnote/android-tutorial-upload-file.html" >7.3.1 Android 文件上传</a></li>
	
		
	<li><a target="_top" data-id="13688" title="7.3.2 Android 文件下载（1）" href="../w3cnote/android-tutorial-download1.html" >7.3.2 Android 文件下载（1）</a></li>
	
		
	<li><a target="_top" data-id="13697" title="7.3.3 Android 文件下载（2）" href="../w3cnote/android-tutorial-download2.html" >7.3.3 Android 文件下载（2）</a></li>
	
		
	<li><a target="_top" data-id="13706" title="7.4 Android 调用 WebService" href="../w3cnote/android-tutorial-webservice.html" >7.4 Android 调用 WebService</a></li>
	
		
	<li><a target="_top" data-id="13724" title="7.5.1 WebView(网页视图)基本用法" href="../w3cnote/android-tutorial-webview.html" >7.5.1 WebView(网页视图)基本用法</a></li>
	
		
	<li><a target="_top" data-id="13731" title="7.5.2 WebView和JavaScrip交互基础" href="../w3cnote/android-tutorial-webview-javascrip.html" >7.5.2 WebView和JavaScrip交互基础</a></li>
	
		
	<li><a target="_top" data-id="13738" title="7.5.3 Android 4.4后WebView的一些注意事项" href="../w3cnote/android-tutorial-webview-attention.html" >7.5.3 Android 4.4后WebView的一些注意事项</a></li>
	
		
	<li><a target="_top" data-id="13748" title="7.5.4 WebView文件下载" href="../w3cnote/android-tutorial-webview-download-file.html" >7.5.4 WebView文件下载</a></li>
	
		
	<li><a target="_top" data-id="13751" title="7.5.5 WebView缓存问题" href="../w3cnote/android-tutorial-webview-cache.html" >7.5.5 WebView缓存问题</a></li>
	
		
	<li><a target="_top" data-id="13756" title="7.5.6 WebView处理网页返回的错误码信息" href="../w3cnote/android-tutorial-webview-error-code.html" >7.5.6 WebView处理网页返回的错误码信息</a></li>
	
		
	<li><a target="_top" data-id="13760" title="7.6.1 Socket学习网络基础准备" href="../w3cnote/android-tutorial-socket-intro.html" >7.6.1 Socket学习网络基础准备</a></li>
	
		
	<li><a target="_top" data-id="13768" title="7.6.2 基于TCP协议的Socket通信(1)" href="../w3cnote/android-tutorial-socket1.html" >7.6.2 基于TCP协议的Socket通信(1)</a></li>
	
		
	<li><a target="_top" data-id="13775" title="7.6.3 基于TCP协议的Socket通信(2)" href="../w3cnote/android-tutorial-socket2.html" >7.6.3 基于TCP协议的Socket通信(2)</a></li>
	
		
	<li><a target="_top" data-id="13788" title="7.6.4 基于UDP协议的Socket通信" href="../w3cnote/android-tutorial-udp-socket.html" >7.6.4 基于UDP协议的Socket通信</a></li>
	
		
	<li><a target="_top" data-id="14013" title="8.1.1 Android中的13种Drawable小结 Part 1" href="../w3cnote/android-tutorial-drawable1.html" >8.1.1 Android中的13种Drawable小结 Part 1</a></li>
	
		
	<li><a target="_top" data-id="14018" title="8.1.2 Android中的13种Drawable小结 Part 2" href="../w3cnote/android-tutorial-drawable2.html" >8.1.2 Android中的13种Drawable小结 Part 2</a></li>
	
		
	<li><a target="_top" data-id="14035" title="8.1.3 Android中的13种Drawable小结 Part 3" href="../w3cnote/android-tutorial-drawable3.html" >8.1.3 Android中的13种Drawable小结 Part 3</a></li>
	
		
	<li><a target="_top" data-id="14092" title="8.2.1 Bitmap(位图)全解析 Part 1" href="../w3cnote/android-tutorial-bitmap1.html" >8.2.1 Bitmap(位图)全解析 Part 1</a></li>
	
		
	<li><a target="_top" data-id="14102" title="8.2.2 Bitmap引起的OOM问题" href="../w3cnote/android-tutorial-bitmap2.html" >8.2.2 Bitmap引起的OOM问题</a></li>
	
		
	<li><a target="_top" data-id="14113" title="8.3.1 三个绘图工具类详解" href="../w3cnote/android-tutorial-drawable-tool.html" >8.3.1 三个绘图工具类详解</a></li>
	
		
	<li><a target="_top" data-id="14144" title="8.3.2 绘图类实战示例" href="../w3cnote/android-tutorial-bitmap-demo.html" >8.3.2 绘图类实战示例</a></li>
	
		
	<li><a target="_top" data-id="14301" title="8.3.3 Paint API之—— MaskFilter(面具)" href="../w3cnote/android-tutorial-maskfilter.html" >8.3.3 Paint API之—— MaskFilter(面具)</a></li>
	
		
	<li><a target="_top" data-id="14307" title="8.3.4 Paint API之—— Xfermode与PorterDuff详解(一)" href="../w3cnote/android-tutorial-xfermode-porterduff.html" >8.3.4 Paint API之—— Xfermode与PorterDuff详解(一)</a></li>
	
		
	<li><a target="_top" data-id="14343" title="8.3.5 Paint API之—— Xfermode与PorterDuff详解(二)" href="../w3cnote/android-tutorial-xfermode-porterduff2.html" >8.3.5 Paint API之—— Xfermode与PorterDuff详解(二)</a></li>
	
		
	<li><a target="_top" data-id="14387" title="8.3.6 Paint API之—— Xfermode与PorterDuff详解(三)" href="../w3cnote/android-tutorial-xfermode-porterduff3.html" >8.3.6 Paint API之—— Xfermode与PorterDuff详解(三)</a></li>
	
		
	<li><a target="_top" data-id="14398" title="8.3.7 Paint API之—— Xfermode与PorterDuff详解(四)" href="../w3cnote/android-tutorial-xfermode-porterduff4.html" >8.3.7 Paint API之—— Xfermode与PorterDuff详解(四)</a></li>
	
		
	<li><a target="_top" data-id="14410" title="8.3.8 Paint API之—— Xfermode与PorterDuff详解(五)" href="../w3cnote/android-tutorial-xfermode-porterduff5.html" >8.3.8 Paint API之—— Xfermode与PorterDuff详解(五)</a></li>
	
		
	<li><a target="_top" data-id="14411" title="8.3.9 Paint API之—— ColorFilter(颜色过滤器)(1/3)" href="../w3cnote/android-tutorial-colorfilter1.html" >8.3.9 Paint API之—— ColorFilter(颜色过滤器)(1/3)</a></li>
	
		
	<li><a target="_top" data-id="14427" title="8.3.10 Paint API之—— ColorFilter(颜色过滤器)(2-3)" href="../w3cnote/android-tutorial-colorfilterl2.html" >8.3.10 Paint API之—— ColorFilter(颜色过滤器)(2-3)</a></li>
	
		
	<li><a target="_top" data-id="14430" title="8.3.11 Paint API之—— ColorFilter(颜色过滤器)(3-3)" href="../w3cnote/android-tutorial-colorfilter3.html" >8.3.11 Paint API之—— ColorFilter(颜色过滤器)(3-3)</a></li>
	
		
	<li><a target="_top" data-id="14517" title="8.3.12 Paint API之—— PathEffect(路径效果)" href="../w3cnote/android-tutorial-patheffect.html" >8.3.12 Paint API之—— PathEffect(路径效果)</a></li>
	
		
	<li><a target="_top" data-id="14520" title="8.3.13 Paint API之—— Shader(图像渲染)" href="../w3cnote/android-tutorial-shader.html" >8.3.13 Paint API之—— Shader(图像渲染)</a></li>
	
		
	<li><a target="_top" data-id="14523" title="8.3.14 Paint几个枚举/常量值以及ShadowLayer阴影效果" href="../w3cnote/android-tutorial-shadowlayer.html" >8.3.14 Paint几个枚举/常量值以及ShadowLayer阴影效果</a></li>
	
		
	<li><a target="_top" data-id="14538" title="8.3.15 Paint API之——Typeface(字型)" href="../w3cnote/android-tutorial-typeface.html" >8.3.15 Paint API之——Typeface(字型)</a></li>
	
		
	<li><a target="_top" data-id="14589" title="8.3.16 Canvas API详解(Part 1)" href="../w3cnote/android-tutorial-canvas-api1.html" >8.3.16 Canvas API详解(Part 1)</a></li>
	
		
	<li><a target="_top" data-id="14678" title="8.3.17 Canvas API详解(Part 2)剪切方法合集" href="../w3cnote/android-tutorial-canvas-api2.html" >8.3.17 Canvas API详解(Part 2)剪切方法合集</a></li>
	
		
	<li><a target="_top" data-id="14692" title="8.3.18 Canvas API详解(Part 3)Matrix和drawBitmapMesh" href="../w3cnote/android-tutorial-canvas-api3.html" >8.3.18 Canvas API详解(Part 3)Matrix和drawBitmapMesh</a></li>
	
		
	<li><a target="_top" data-id="14718" title="8.4.1 Android动画合集之帧动画" href="../w3cnote/android-tutorial-animation.html" >8.4.1 Android动画合集之帧动画</a></li>
	
		
	<li><a target="_top" data-id="14722" title="8.4.2 Android动画合集之补间动画" href="../w3cnote/android-tutorial-alphaanimation.html" >8.4.2 Android动画合集之补间动画</a></li>
	
		
	<li><a target="_top" data-id="14730" title="8.4.3 Android动画合集之属性动画-初见" href="../w3cnote/android-tutorial-valueanimator.html" >8.4.3 Android动画合集之属性动画-初见</a></li>
	
		
	<li><a target="_top" data-id="14768" title="8.4.4 Android动画合集之属性动画-又见" href="../w3cnote/android-tutorial-valueanimator2.html" >8.4.4 Android动画合集之属性动画-又见</a></li>
	
		
	<li><a target="_top" data-id="14149" title="9.1 使用SoundPool播放音效(Duang~)" href="../w3cnote/android-tutorial-soundpool.html" >9.1 使用SoundPool播放音效(Duang~)</a></li>
	
		
	<li><a target="_top" data-id="14612" title="9.2 MediaPlayer播放音频与视频" href="../w3cnote/android-tutorial-mediaplayer.html" >9.2 MediaPlayer播放音频与视频</a></li>
	
		
	<li><a target="_top" data-id="14831" title="9.3 使用Camera拍照" href="../w3cnote/android-tutorial-camera.html" >9.3 使用Camera拍照</a></li>
	
		
	<li><a target="_top" data-id="14832" title="9.4 使用MediaRecord录音" href="../w3cnote/android-tutorial-mediarecord.html" >9.4 使用MediaRecord录音</a></li>
	
		
	<li><a target="_top" data-id="14156" title="10.1 TelephonyManager(电话管理器)" href="../w3cnote/android-tutorial-telephonymanager.html" >10.1 TelephonyManager(电话管理器)</a></li>
	
		
	<li><a target="_top" data-id="14163" title="10.2 SmsManager(短信管理器)" href="../w3cnote/android-tutorial-smsmanager.html" >10.2 SmsManager(短信管理器)</a></li>
	
		
	<li><a target="_top" data-id="14166" title="10.3 AudioManager(音频管理器)" href="../w3cnote/android-tutorial-audiomanager.html" >10.3 AudioManager(音频管理器)</a></li>
	
		
	<li><a target="_top" data-id="14319" title="10.4 Vibrator(振动器)" href="../w3cnote/android-tutorial-vibrator.html" >10.4 Vibrator(振动器)</a></li>
	
		
	<li><a target="_top" data-id="14400" title="10.5 AlarmManager(闹钟服务)" href="../w3cnote/android-tutorial-alarmmanager.html" >10.5 AlarmManager(闹钟服务)</a></li>
	
		
	<li><a target="_top" data-id="14438" title="10.6 PowerManager(电源服务)" href="../w3cnote/android-tutorial-powermanager.html" >10.6 PowerManager(电源服务)</a></li>
	
		
	<li><a target="_top" data-id="14697" title="10.7 WindowManager(窗口管理服务)" href="../w3cnote/android-tutorial-windowmanager.html" >10.7 WindowManager(窗口管理服务)</a></li>
	
		
	<li><a target="_top" data-id="14702" title="10.8 LayoutInflater(布局服务)" href="../w3cnote/android-tutorial-layoutinflater.html" >10.8 LayoutInflater(布局服务)</a></li>
	
		
	<li><a target="_top" data-id="14737" title="10.9 WallpaperManager(壁纸管理器)" href="../w3cnote/android-tutorial-wallpapermanager.html" >10.9 WallpaperManager(壁纸管理器)</a></li>
	
		
	<li><a target="_top" data-id="14743" title="10.10 传感器专题(1)——相关介绍" href="../w3cnote/android-tutorial-sensor1.html" >10.10 传感器专题(1)——相关介绍</a></li>
	
		
	<li><a target="_top" data-id="14746" title="10.11 传感器专题(2)——方向传感器" href="../w3cnote/android-tutorial-sensor2.html" >10.11 传感器专题(2)——方向传感器</a></li>
	
		
	<li><a target="_top" data-id="14778" title="10.12 传感器专题(3)——加速度/陀螺仪传感器" href="../w3cnote/android-tutorial-sensor3.html" >10.12 传感器专题(3)——加速度/陀螺仪传感器</a></li>
	
		
	<li><a target="_top" data-id="14835" title="10.12 传感器专题(4)——其他传感器了解" href="../w3cnote/android-tutorial-sensor4.html" >10.12 传感器专题(4)——其他传感器了解</a></li>
	
		
	<li><a target="_top" data-id="14837" title="10.14 Android GPS初涉" href="../w3cnote/android-tutorial-gps.html" >10.14 Android GPS初涉</a></li>
	
		
	<li><a target="_top" data-id="14846" title="11.0《2015最新Android基础入门教程》完结散花~" href="../w3cnote/android-tutorial-end.html" >11.0《2015最新Android基础入门教程》完结散花~</a></li>
	
		
	<li><a target="_top" data-id="19314" title="12.1Android 实战 ：DrySister看妹子应用(第一版) -- 项目搭建与简单实现" href="../w3cnote/android-tutorial-exercise-1.html" >12.1Android 实战 ：DrySister看妹子应用(第一版) -- 项目搭建与简单实现</a></li>
	
		
	<li><a target="_top" data-id="19321" title="12.2 DrySister看妹子应用(第一版)——2.解析后台数据" href="../w3cnote/android-tutorial-exercise-2.html" >12.2 DrySister看妹子应用(第一版)——2.解析后台数据</a></li>
	
		
	<li><a target="_top" data-id="19334" title="12.3 DrySister看妹子应用(第一版)——3.图片加载优化(写个图片缓存小框架)" href="../w3cnote/android-tutorial-exercise-3.html" >12.3 DrySister看妹子应用(第一版)——3.图片加载优化(写个图片缓存小框架)</a></li>
	
		
	<li><a target="_top" data-id="19349" title="12.4 DrySister看妹子应用(第一版)——4.添加数据缓存(加入SQLite)" href="../w3cnote/android-tutorial-exercise-4.html" >12.4 DrySister看妹子应用(第一版)——4.添加数据缓存(加入SQLite)</a></li>
	
		
	<li><a target="_top" data-id="19355" title="12.5 DrySister看妹子应用(第一版)——5.代码回顾，调整与日志类编写" href="../w3cnote/android-tutorial-exercise-5.html" >12.5 DrySister看妹子应用(第一版)——5.代码回顾，调整与日志类编写</a></li>
	
		
	<li><a target="_top" data-id="19386" title="12.6 DrySister看妹子应用(第一版)——6.图标制作，混淆，签名打包，APK瘦身，应用发布" href="../w3cnote/android-tutorial-exercise-6-html.html" >12.6 DrySister看妹子应用(第一版)——6.图标制作，混淆，签名打包，APK瘦身，应用发布</a></li>
	
	</ul></div>	</div>
</div>


<!-- 底部 -->
<div id="footer" class="mar-t50">
   <div class="runoob-block">
    <div class="runoob cf">
     <dl>
      <dt>
       在线实例
      </dt>
      <dd>
       &middot;<a target="_blank" href="../html/html-examples.html">HTML 实例</a>
      </dd>
      <dd>
       &middot;<a target="_blank" href="../css/css-examples.html">CSS 实例</a>
      </dd>
      <dd>
       &middot;<a target="_blank" href="../js/js-examples.html">JavaScript 实例</a>
      </dd>
      <dd>
       &middot;<a target="_blank" href="../ajx/ajax-examples.html">Ajax 实例</a>
      </dd>
       <dd>
       &middot;<a target="_blank" href="../jquery/jquery-examples.html">jQuery 实例</a>
      </dd>
      <dd>
       &middot;<a target="_blank" href="../xml/xml-examples.html">XML 实例</a>
      </dd>
      <dd>
       &middot;<a target="_blank" href="../java/java-examples.html">Java 实例</a>
      </dd>
     
     </dl>
     <dl>
      <dt>
      字符集&工具
      </dt>
      <dd>
       &middot; <a target="_blank" href="../charsets/html-charsets.html">HTML 字符集设置</a>
      </dd>
      <dd>
       &middot; <a target="_blank" href="../tags/html-ascii.html">HTML ASCII 字符集</a>
      </dd>
     <dd>
       &middot; <a target="_blank" href="https://c.runoob.com/front-end/6939/">JS 混淆/加密</a>
      </dd> 
      <dd>
       &middot; <a target="_blank" href="https://c.runoob.com/front-end/6232/">PNG/JPEG 图片压缩</a>
      </dd>
      <dd>
       &middot; <a target="_blank" href="../tags/html-colorpicker.html">HTML 拾色器</a>
      </dd>
      <dd>
       &middot; <a target="_blank" href="..//c.runoob.com/front-end/53">JSON 格式化工具</a>
      </dd>
      <dd>
       &middot; <a target="_blank" href="..//c.runoob.com/front-end/6680/">随机数生成器</a>
      </dd>
     </dl>
     <dl>
      <dt>
       最新更新
      </dt>
                   <dd>
       &middot;
      <a href="../matplotlib/matplotlib-imread.html" title="Matplotlib imread() 方法">Matplotlib imre...</a>
      </dd>
              <dd>
       &middot;
      <a href="../matplotlib/matplotlib-imsave.html" title="Matplotlib imsave() 方法">Matplotlib imsa...</a>
      </dd>
              <dd>
       &middot;
      <a href="../matplotlib/matplotlib-imshow.html" title="Matplotlib imshow() 方法">Matplotlib imsh...</a>
      </dd>
              <dd>
       &middot;
      <a href="../matplotlib/matplotlib-hist.html" title="Matplotlib 直方图">Matplotlib 直方图</a>
      </dd>
              <dd>
       &middot;
      <a href="../python3/python-func-object.html" title="Python object() 函数">Python object()...</a>
      </dd>
              <dd>
       &middot;
      <a href="../python3/python-ai-draw.html" title="Python AI 绘画">Python AI 绘画</a>
      </dd>
              <dd>
       &middot;
      <a href="../w3cnote/cursor-editor.html" title="神辅助 Cursor 编辑器，加入 GPT-4 让编码更轻松！">神辅助 Cursor ...</a>
      </dd>
             </dl>
     <dl>
      <dt>
       站点信息
      </dt>
      <dd>
       &middot;
       <a target="_blank" href="mailto:admin@runoob.com" rel="external nofollow">意见反馈</a>
       </dd>
      <dd>
       &middot;
      <a target="_blank" href="../disclaimer">免责声明</a>
       </dd>
      <dd>
       &middot;
       <a target="_blank" href="../aboutus">关于我们</a>
       </dd>
      <dd>
       &middot;
      <a target="_blank" href="../archives">文章归档</a>
      </dd>
    
     </dl>
    
     <div class="search-share">
      <div class="app-download">
        <div>
         <strong>关注微信</strong>
        </div>
      </div>
      <div class="share">
      <img width="128" height="128" src="/wp-content/themes/runoob/assets/images/qrcode.png" />
       </div>
     </div>
     
    </div>
   </div>
   <div class="w-1000 copyright">
     Copyright &copy; 2013-2023    <strong><a href="../" target="_blank">菜鸟教程</a></strong>&nbsp;
    <strong><a href="../" target="_blank">runoob.com</a></strong> All Rights Reserved. 备案号：<a target="_blank" rel="nofollow" href="https://beian.miit.gov.cn/">闽ICP备15012807号-1</a>
   </div>
  </div>
  <div class="fixed-btn">
    <a class="go-top" href="javascript:void(0)" title="返回顶部"> <i class="fa fa-angle-up"></i></a>
    <a class="qrcode"  href="javascript:void(0)" title="关注我们"><i class="fa fa-qrcode"></i></a>
    <a class="writer" style="display:none" href="javascript:void(0)"   title="标记/收藏"><i class="fa fa-star" aria-hidden="true"></i></a>
    <!-- qrcode modal -->
    <div id="bottom-qrcode" class="modal panel-modal hide fade in">
      <h4>微信关注</h4>
      <div class="panel-body"><img alt="微信关注" width="128" height="128" src="/wp-content/themes/runoob/assets/images/qrcode.png"></div> 
    </div>
  </div>

 <div style="display:none;">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-84264393-2"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-84264393-2');
</script>
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?3eec0b7da6548cf07db3bc477ea905ee";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>

</div>
<script>
window.jsui={
    www: 'https://www.runoob.com',
    uri: 'https://www.runoob.com/wp-content/themes/runoob'
};
</script>

<script src=""></script>
<script src=""></script>

</body>
</html>